package factory;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import util.fraction;
import value_object.InstanceVO;
import value_object.ResultVO;
import value_object.SolutionVO;
import value_object.data.Request;
import value_object.data.Strip;


public class ResultFactory {
	
	private InstanceVO instance;
	private ArrayList<Integer> request_index;
	private Map<Integer, Double> myHashtable; //request_index as a key and sum of surface as a value
	
	
	public ArrayList<Integer> getRequest_index() {
		return request_index;
	}

	public void setRequest_index(ArrayList<Integer> request_index) {
		this.request_index = request_index;
	}

	public Map<Integer, Double> getMyHashtable() {
		return myHashtable;
	}

	public void setMyHashtable(Map<Integer, Double> myHashtable) {
		this.myHashtable = myHashtable;
	}

	public ResultFactory(InstanceVO instance) {
		this.instance = instance;
		this.request_index = new ArrayList <Integer> ();
		this.myHashtable = new Hashtable <Integer, Double>();
	}
	
	/**
	 * Get result file
	 * 
	 * @param solution
	 * @return
	 */
	public ResultVO getResult(SolutionVO solution) {
		ResultVO result = 
			new ResultVO(solution.getData_set_number(), solution.getGrid_number(), solution.getTrack_number());
		
		collectRequestIndexAndSumIn(instance, solution);
		
		result.setGain(calculateTotalGain(instance, solution));
		System.out.println(calculateTotalGain(instance, solution));
		return result;
	}
	
	
	/**
	 * Collect the index request in solution file and the sum of surface corresponding
	 * 
	 * @param instance
	 * @param solution
	 */
	public void collectRequestIndexAndSumIn(InstanceVO instance, SolutionVO solution){
		
		//Here, we take all the index request (in solution file)
		List<Strip> stripListOfSolution = instance.getStripListFrom(solution);
		request_index.add(stripListOfSolution.get(0).getRequest_index());
		
		for(int i=0; i< stripListOfSolution.size(); i++){
			if(!request_index.contains(stripListOfSolution.get(i).getRequest_index()))
				request_index.add(stripListOfSolution.get(i).getRequest_index());
		}
		
		//Here, we associated each index request with the sum of strips'list
		double somme = 0;
		int i=0;
		while(i<request_index.size()){
			for(int j=0; j<stripListOfSolution.size(); j++){
				if(request_index.get(i).equals(stripListOfSolution.get(j).getRequest_index()))
				{
					somme = somme + stripListOfSolution.get(j).getStrip_surface();
					myHashtable.put(stripListOfSolution.get(j).getRequest_index(), somme);
				}
			}
			somme = 0;
			i++;
		}
	}
	
	/**
	 * Calculate the total gain
	 * 
	 * @param instance
	 * @param solution
	 * @return
	 */
	
	public double calculateTotalGain(InstanceVO instance, SolutionVO solution){
		List<Request> requestListOfInstance = instance.getRequestList();
		Set<Integer> set = myHashtable.keySet();
	    Iterator<Integer> itr = set.iterator();
	    Integer valeur;
	    double somme = 0;
	    
	    while (itr.hasNext()) {
		   valeur = itr.next();
		   for(int i=0; i<requestListOfInstance.size(); i++){
				if(valeur == requestListOfInstance.get(i).getRequest_index())
					somme += calculateGain(requestListOfInstance.get(i), myHashtable.get(valeur));
		   }
		}
	    return somme;
	}
	
	
	/**
	 * Calculate the gain (both polygon and normal requests)
	 * 
	 * @param req (request in instance file)
	 * @param value (value corresponding to a request)
	 * @return
	 */
	private double calculateGain(Request req, double value){
		//normal request
		if(req.getRequest_type() == 0)
			return calculateGainOfNormal(req);
		
		//polygone request
		if(req.getRequest_type() == 2)
			return calculateGainOfPolygon(req, value);
		
		return 0;
	}
	
	/**
	 * Calculate the gain of normal request
	 * 
	 * @param req
	 * @return
	 */
	private double calculateGainOfNormal(Request req) {
		if(req.getRequest_stereo() == 0) //request is mono
			return req.getRequest_gain()* req.getRequest_surface();
		
		if(req.getRequest_stereo() == 1) //request is stereo
			return req.getRequest_gain()* req.getRequest_surface() * 2;
		
		return 0;
	}
	
	/**
	 * Calculate the gain of polygon request
	 * 
	 * @param req
	 * @param value
	 * @return
	 */
	private double calculateGainOfPolygon(Request req, double value){
		double gain = 0;

		if(req.getRequest_stereo() == 0) //request is mono
			gain += req.getRequest_gain() * req.getRequest_surface() * gainOfPartialAcquisitionPolygon(req, value);
		else
			gain += req.getRequest_gain() * req.getRequest_surface() * 2 * gainOfPartialAcquisitionPolygon(req, value);
		
		return gain;	
	}	 

	/**
	 * Calculate the gain partial of polygon request
	 * 
	 * @param req
	 * @param value (it represents the sum of the strips'surface) 
	 * @return
	 */
	private double gainOfPartialAcquisitionPolygon(Request req, double value){
		double fractionvalue = 0;
		double result = 0;
		
		if(req.getRequest_stereo() == 0) //request is mono
			fractionvalue = value / req.getRequest_surface();
		if(req.getRequest_stereo() == 1) //request is mono
			fractionvalue = value / (2*req.getRequest_surface());
		
		if (fractionvalue >= 0 && fractionvalue <= 0.4)
			result = fraction.gainOfPartialAcquisition(1, fractionvalue);
		if (fractionvalue > 0.4 && fractionvalue < 0.7)
			result = fraction.gainOfPartialAcquisition(2, fractionvalue);
		if (fractionvalue >= 0.7 && fractionvalue <= 1)
			result = fraction.gainOfPartialAcquisition(3, fractionvalue);
		return result;
	}
	
}



